/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { useControlController, useNamespace } from '@ibiz-template/vue3-util';
import {
  defineComponent,
  h,
  PropType,
  reactive,
  ref,
  renderSlot,
  resolveComponent,
  VNode,
  watch,
} from 'vue';
import {
  ControllerEvent,
  EditFormController,
  IControlProvider,
  IDRBarController,
  ScriptFactory,
  findChildFormDetails,
} from '@ibiz-template/runtime';
import { createUUID } from 'qx-util';
import { IDEEditForm, IDEFormDetail, IDEFormItem } from '@ibiz/model-core';
import { FormAnchorPointEvent } from './form-anchor-point.event';
import './form-anchor-point.scss';

export const FormAnchorPointControl = defineComponent({
  name: 'IBizFormAnchorPoint',
  props: {
    modelData: {
      type: Object as PropType<IDEEditForm>,
      required: true,
    },
    context: { type: Object as PropType<IContext>, required: true },
    params: { type: Object as PropType<IParams>, default: () => ({}) },
    provider: { type: Object as PropType<IControlProvider> },
    isSimple: { type: Boolean, required: false },
    data: { type: Object as PropType<IData>, required: false },
    loadDefault: { type: Boolean, default: true },
  },
  setup(props, { slots }) {
    const c = useControlController(
      (...args) => new EditFormController(...args),
      { excludePropsKeys: ['data'] },
    );

    const uuid = ref(createUUID());

    if (props.isSimple) {
      c.evt.on('onMounted', () => {
        // 第一次data直接赋值，后面默认加载会走load
        c.setSimpleData(props.data || {});
      });
      watch(
        () => props.data,
        newVal => {
          const changeVal = newVal || {};
          // 找有没有不一致的属性
          const find = Object.keys(c.data).find(key => {
            return changeVal[key] !== c.data[key];
          });
          // 内外部数据不一致时，只能是外部修改了，这是更新数据并重走load
          if (find) {
            c.setSimpleData(changeVal);
          }
        },
        { deep: true },
      );
    }

    const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`);

    c.evt.on('onCreated', () => {
      // 表单成员state响应式
      const keys = Object.keys(c.details);
      keys.forEach(key => {
        const detail = c.details[key];
        detail.state = reactive(detail.state);
      });
    });

    const ns2 = useNamespace(`form-anchor-point`);

    const drBar = c.getController('drbar') as IDRBarController;

    const items = ref([]);

    const formAnchorPoint = ref<HTMLDivElement | null>(null);

    const newId = ref('');

    // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-function-return-type
    const throttle = (func: Function, delay: number) => {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      let timeoutId: any;

      return (...args: []): void => {
        // 如果已经有延迟任务存在，则取消它
        if (timeoutId) {
          clearTimeout(timeoutId);
        }

        // 设置新的延迟任务
        timeoutId = setTimeout(() => {
          func.apply(this, args);
          timeoutId = undefined; // 执行完后清除 timeoutId
        }, delay);
      };
    };
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
    const clearNewId = () => {
      newId.value = '';
    };

    const calcPermitted = () => {
      if (drBar) {
        const dedrctrlItems = (drBar.model as IData).dedrctrlItems || [];
        const result = dedrctrlItems.map(item => {
          const data = drBar.state.drBarItems.find(x => x.tag === item.id);
          return { ...item, ...data };
        });
        items.value = result;
      }
    };

    const throttledClearNewId = throttle(clearNewId, 2500);

    watch(newId, (newValue, _oldValue) => {
      if (newValue) {
        throttledClearNewId();
      }
    });

    watch(
      () => drBar.state.drBarItems,
      (newValue, _oldValue) => {
        if (newValue) {
          calcPermitted();
        }
      },
      { deep: true },
    );

    const onClickAnchor = (id: string): void => {
      if (!formAnchorPoint.value) {
        return;
      }
      newId.value = id;
      const scrollArea = formAnchorPoint.value;
      const children = Array.from(scrollArea.children);
      const el = children.find(item => item.getAttribute('data-id') === id);
      if (el) {
        if ('scrollBehavior' in document.documentElement.style) {
          el.scrollIntoView({
            block: 'start',
            inline: 'nearest',
            behavior: 'smooth',
          });
        }
      }
    };

    const isCollapse = ref(false);

    const evt = c.view.evt as ControllerEvent<FormAnchorPointEvent>;

    evt.on('onClickAnchor', data => {
      if (data.id) {
        onClickAnchor(data.id);
      }
    });

    evt.on('onClickShrink', data => {
      isCollapse.value = data.shrink;
    });

    const isLoadSuccess = ref(false);

    c.evt.on('onLoadSuccess', () => {
      if (!isLoadSuccess.value) {
        isLoadSuccess.value = true;
      } else {
        uuid.value = createUUID();
      }
    });

    /** 作用域插槽提供的参数 */
    const slotProps: IData = { form: c };

    /**
     * 绘制成员的attrs
     * @author lxm
     * @date 2024-03-19 03:48:00
     * @param {IDEFormDetail} model
     * @return {*}  {IParams}
     */
    const renderAttrs = (model: IDEFormDetail): IParams => {
      const attrs: IParams = {};
      model.controlAttributes?.forEach(item => {
        if (item.attrName && item.attrValue) {
          attrs[item.attrName!] = ScriptFactory.execSingleLine(
            item.attrValue!,
            {
              ...c.getEventArgs(),
              data: c.data,
            },
          );
        }
      });
      return attrs;
    };

    /**
     * 按照类型绘制表单成员
     * @author lxm
     * @date 2023-06-12 06:24:14
     * @param {IDEFormDetail} detail
     * @return {*}  {(VNode | VNode[] | undefined)}
     */
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
    const renderByDetailType = (detail: IDEFormDetail) => {
      if ((detail as IDEFormItem).hidden) {
        return;
      }
      const detailId = detail.id!;

      // 表单成员组件的props
      const detailProps: IData = {
        modelData: detail,
        controller: c.details[detailId],
        key: detail.id,
        attrs: renderAttrs(detail),
      };

      // 有插槽走插槽
      if (slots[detailId]) {
        return renderSlot(slots, detailId, {
          ...slotProps,
          ...detailProps,
        });
      }

      // 子插槽
      const childSlots: IData = {};
      // 表单项如果有编辑器插槽的时候，调用插槽绘制表单项的默认插槽。
      if (detail.detailType === 'FORMITEM' && slots[`${detailId}_editor`]) {
        childSlots.default = (...args: IData[]): VNode[] => {
          return slots[`${detailId}_editor`]!(...args);
        };
      }
      const childDetails = findChildFormDetails(detail);
      if (childDetails.length) {
        // 容器成员绘制子成员
        // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
        childSlots.default = () =>
          childDetails.map(child => {
            return renderByDetailType(child);
          });
      }

      // 根据适配器绘制表单成员
      const provider = c.providers[detailId];
      if (!provider) {
        return (
          <div>
            {ibiz.i18n.t('control.form.noSupportDetailType', {
              detailType: detail.detailType,
            })}
          </div>
        );
      }
      const component = resolveComponent(provider.component) as string;
      return h(
        component,
        {
          ...detailProps,
        },
        childSlots,
      );
    };

    /** 内置表单成员组件 */
    const FormDetail = (_props: {
      modelData: IDEFormDetail | IDEFormDetail[];
      // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
    }) => {
      const { modelData } = _props;
      const detailModels = modelData instanceof Array ? modelData : [modelData];
      return detailModels.map(detail => {
        return renderByDetailType(detail);
      });
    };
    FormDetail.props = ['modelData'];

    slotProps.FormDetail = FormDetail;

    const pageIndex = ref(0);

    const handlePageChange = (_id: string, index: number): void => {
      pageIndex.value = index;
    };

    return {
      c,
      ns,
      ns2,
      uuid,
      formAnchorPoint,
      items,
      pageIndex,
      renderByDetailType,
      handlePageChange,
      isCollapse,
      newId,
      isLoadSuccess,
    };
  },

  render() {
    return (
      <div class={[this.ns2.b(), this.isCollapse ? 'isCollapse' : '']}>
        <iBizFormControl
          class={[this.ns.b(), this.ns2.b('form')]}
          controller={this.c}
        >
          {{
            default: () => {
              return (
                <iBizCrmPage
                  modelData={this.c.model}
                  controller={this.c}
                  onTabChange={this.handlePageChange}
                >
                  {this.c.model.deformPages?.map(page => {
                    return this.renderByDetailType(page);
                  })}
                </iBizCrmPage>
              );
            },
            ...this.$slots,
          }}
        </iBizFormControl>
        {this.pageIndex === 0 && (
          <div class={this.ns2.b('content')} ref='formAnchorPoint'>
            {this.isLoadSuccess &&
              this.items.map((item: IData) => {
                if (!item.visible) {
                  return null;
                }
                return (
                  <div class={this.ns2.b('item')} data-id={item.id}>
                    {h(resolveComponent('IBizViewShell'), {
                      context: this.c.context,
                      params: this.c.params,
                      key: `${item.id}_${this.uuid}`,
                      viewId: item.appViewId,
                      class: `${this.ns2.b('form-view')} ${
                        this.newId === item.id ? 'isShowHighLight' : ''
                      }`,
                    })}
                  </div>
                );
              })}
          </div>
        )}
      </div>
    );
  },
});
